﻿using System;
using System.Linq;
using System.Web;
using System.Web.Caching;
using System.Web.Routing;
using System.Web.Mvc;

using Fuse8.ContentManagementFramework.BizFacade.Services;
using Fuse8.ContentManagementFramework.Domain;
using Fuse8.ContentManagementFramework.Domain.Special;
using Fuse8.ContentManagementFramework.Web.StartUp;
using Fuse8.DomainFramework.Common.Locator;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    public static class CmsRoute
    {
        #region Consts

        public const string AdministrationAreaName = "Cms administration";

        #endregion

        #region Fields

        private static CmsPageService _service = new CmsPageService();

        private static RouteBase[] _nonCmsPageRoutes;

        private static ICustomRouteService routeService;

        #endregion

        #region Properties

        public static RouteCollection RouteCollection
        {
            get;
            private set;
        }

        public static ICustomRouteService CustomRouteService
        {
            get
            {
                if (routeService == null)
                {
                    if (DependencyServiceLocator.Current.IsRegistered<ICustomRouteService>())
                    {
                        routeService = DependencyServiceLocator.Current.GetInstance<ICustomRouteService>();
                    }
                    else
                    {
                        routeService = new DefaultCustomRouteService();
                    }
                }

                return routeService;
            }
        }

        #endregion

        #region Constructor

        static CmsRoute()
        {
            //Takes initial routes here
            _nonCmsPageRoutes = new RouteBase[RouteTable.Routes.Count];
            RouteTable.Routes.CopyTo(_nonCmsPageRoutes, 0);
            
            //Init empty collection
            RouteCollection = new RouteCollection();

        	UpdateRoutes();
        }

        #endregion

        #region Methods

        /// <summary>
        /// Builds RouteTable
        /// </summary>
		public static void UpdateRoutes()
		{
            RouteCollection.Clear();

			//Now load cms pages related routes (page's url)
			LoadCmsPages();

			//Load common routes back
			LoadNonCmsRoutes();

            using (RouteTable.Routes.GetReadLock())
            {
                RouteTable.Routes.Clear();

                int count = RouteCollection.Count;

                RouteTable.Routes.MapRoute("CMS:CONTENT:MODULE", "CmsContentModule/RenderContentModule",
                                           new { controller = "CmsContentModule", action = "RenderContentModule", area = CmsPolicy.FrontEndArea },
                                           new string[] {"Fuse8.ContentManagementFramework.Web.ApplicationRuntime"});

                for (int i = 0; i < count; i++)
                {
                	var route = RouteCollection[i] as INamedRoute;
					if (route != null)
					{
						RouteTable.Routes.Add(route.Name, RouteCollection[i]);	
					}
                    else
					{
						RouteTable.Routes.Add(RouteCollection[i]);	
					}
                }
            }            
		}

        /// <summary>
        /// Clears current RouteTable and builds it again
        /// </summary>
        public static void ClearByDependency()
        {
            using (RouteCollection.GetWriteLock())
            {
                RouteCollection.Clear();

				UpdateRoutes();
            }
        }

        /// <summary>
        /// Builds route name by page and its adopted URL
        /// </summary>
        /// <param name="pageUrl">Adopted Url</param>
        /// <param name="item">CmsPage</param>
        /// <returns>Concatinating od page name and url</returns>
        public static string BuildRouteName(string pageUrl, CmsPage item)
        {
            return String.Format("{0}_{1}", item.PageName, pageUrl);
        }

        /// <summary>
        /// Adopts url of CmsPage to route URL scheme
        /// </summary>
        /// <param name="item">CmsPage</param>
        /// <returns>PageUrl without start slash</returns>
        public static string AdoptPageUrlToRoute(string pageUrl)
        {
            return pageUrl.TrimStart(new char[] { '/' });
        }

        public static RouteData GetRouteData(HttpContextBase context)
        {
            var key = "CustomRoutePage_" + context.Request.RawUrl;
            var page = context.Cache[key] as CmsPage;
            if (page == null)
            {
                var cutomRoute = CustomRouteService.GetCustomRouteData(context);                
                if (cutomRoute != null && cutomRoute.PublicCmsPageId.HasValue)
                {
                    var pageHandler = new MvcRouteHandler();
                    var defaults = new RouteValueDictionary(cutomRoute.Defaults)
                                       {{"controller", "CmsBaseFrontEnd"}, {"action", "Index"}};

                    var dataTokens = new RouteValueDictionary
                                         {
                                             {CmsRequestData.UseFileCacheKey, cutomRoute.UseFileCache},
                                             {CmsRequestData.ContextPageId, cutomRoute.PublicCmsPageId}
                                         };

                    page = _service.FindByPublicId(cutomRoute.PublicCmsPageId.Value);
                    var pageUrl = AdoptPageUrlToRoute(cutomRoute.Url);

                    string routeName = BuildRouteName(pageUrl, page);

                    if (RouteCollection[routeName] == null)
                    {
                        var route = new Route(pageUrl, pageHandler)
                                        {
                                            Defaults = defaults,
                                            DataTokens = dataTokens
                                        };
                        RouteCollection.Insert(0, route);

                        RouteTable.Routes.Insert(0, route);
                    }

                    DependencyServiceLocator.Current.GetInstance<ICmsContext>().ContextPage = page;
                }
                else
                {
                    page = new CmsPage();
                }

                context.Cache.Add(key, page, null, DateTime.Now.AddMinutes(10), Cache.NoSlidingExpiration,CacheItemPriority.Normal, null);
            }
            else if (page.IsCreated)
            {
                DependencyServiceLocator.Current.GetInstance<ICmsContext>().ContextPage = page;
            }

            RouteData result = RouteCollection.GetRouteData(context);
            return result;
        }

        private static bool IsAdministrationAreaRoute(RouteBase route)
        {
            return route is ICmsAdministrationRoute;
        }

        private static bool IsCustomAdministrationAreaRoute(RouteBase route)
        {
            return route is ICmsCustomAdministrationRoute;
        }

        private static void LoadNonCmsRoutes()
        {
            _nonCmsPageRoutes = _nonCmsPageRoutes                
                .OrderByDescending(IsAdministrationAreaRoute)
                .OrderByDescending(IsCustomAdministrationAreaRoute)
                .ToArray();

            foreach (var route in _nonCmsPageRoutes)
            {
                RouteCollection.Add(route);
            }
        }

        private static void LoadCmsPages()
        {
            var pageRoutes = _service.GetPageRoutes();

            var defaults = new RouteValueDictionary((object)null);
            foreach (var item in pageRoutes.Where(CanBeAddedInRouteTable))
            {
                IRouteHandler pageHandler;
                switch (item.CommonPageType)
                {
                    case CmsPageTypeKind.AspNetForm:
                        pageHandler = new CmsFormPageUrlRouteHandler();
                        break;
                    case CmsPageTypeKind.AspNetMvc:
                        pageHandler = new MvcRouteHandler();
                        defaults = new RouteValueDictionary(
                            new { controller = "CmsBaseFrontEnd", action = "Index" });
                        break;
                    default:
                        pageHandler = new StopRoutingHandler();
                        break;
                }

                var dataTokens = new RouteValueDictionary
                                     {
                                         {CmsRequestData.UseFileCacheKey, item.UseFileCache},
                                         {CmsRequestData.ContextPageId, item.Id}
                                     };

                string pageUrl = AdoptPageUrlToRoute(item.PageUrl);

                string routeName = BuildRouteName(pageUrl, item);

                if (RouteCollection[routeName] == null)
                {
                    RouteCollection.Add(
                        routeName,
                        new Route(pageUrl, pageHandler)
                        {
                            Defaults = defaults,
                            DataTokens = dataTokens
                        });
                }
                else
                { 
                    //TO DO: log this
                }
            }
        }

        private static bool CanBeAddedInRouteTable(CmsPage cmsPage)
        {
            return cmsPage.BehaviourPageType == CmsPageBehaviourTypeKind.Content ||
                   cmsPage.BehaviourPageType == CmsPageBehaviourTypeKind.Redirect && cmsPage.RedirectSetting != null &&
                   cmsPage.RedirectSetting.Type != PageRedirectKind.PresentOnly;
        }

        #endregion
    }
}
